home *** CD-ROM | disk | FTP | other *** search
/ Aminet 45 / Aminet 45 (2001)(GTI - Schatztruhe)[!][Oct 2001].iso / Aminet / game / role / ldmud-3.2-bin.lha / mud / doc / LPC / closure_guide < prev    next >
Text File  |  2001-04-06  |  31KB  |  714 lines

  1. Closure Guide for LPC
  2.  
  3. Table of Contents
  4.  
  5.         1 Indroduction, Overview and Efun-Closures
  6.  
  7.         2 Lfun-, Inline and Lambda-Closures
  8.         2.1 Lfun-Closures
  9.         2.2 Inline-Closures
  10.         2.3 Lambda-Closures
  11.         2.3.1 Advantages of Lambda-Closures
  12.         2.3.2 Free Variables in Lambda-Closure Constructs
  13.         2.3.3 Special Efun-Closures and Operator-Closures for Lambdas
  14.         2.4 Closures with Strange Names
  15.         2.5 Operator-Closures
  16.         2.6 Variable-Closures
  17.  
  18.         3 Examples
  19.         3.1 Lfun-Closure
  20.         3.2 Lambda-Closure
  21.  
  22. 1 Introduction, Overview and Efun-Closures
  23.  
  24.   A closure is a pointer to a function. That means that it is data like an
  25.   int or a string are. It may be assigned to a variable or given to anoth-
  26.   er function as argument.
  27.  
  28.   To create a closure that points to an efun like write() you can write
  29.   the name of the efun prepended with "hash-tick": #'. #'write is a clo-
  30.   sure that points to the efun write().
  31.  
  32.   I very often put parentheses around such a closure-notation because
  33.   otherwise my editor gets confused by the hashmark: (#'write). This is
  34.   especially of interest within lambda-closures (see below).
  35.  
  36.   A closure can be evaluated (which means that the function it points to
  37.   is called) using the efuns funcall() or apply(), which also allow to
  38.   give arguments to the function. Example:
  39.  
  40.     funcall(#'write,"hello");
  41.  
  42.   This will result in the same as write("hello"); alone. The string
  43.   "hello" is given as first (and only) argument to the function the clo-
  44.   sure #'write points to.
  45.  
  46.   The return value of the function the closure points to is returned by
  47.   the efun funcall() or apply(). (Since write() always returns 0 the re-
  48.   turn value of the example above will be 0.)
  49.  
  50.   What are closures good for? With closures you can make much more univer-
  51.   sally usable functions. A good example is the function filter_array().
  52.   It gets an array and a closure as arguments. Then it calls the function
  53.   the closure points to for each element of the array:
  54.  
  55.     filter_array(({ "bla","foo","bar" }),#'write);
  56.  
  57.   This will call write("bla"), write("foo") and write("bar") in any order;
  58.   the order is undefined.
  59.   (In the current implementation the given closure is evaluated for all
  60.   elements from the first to the last, so the output will be "blafoobar".)
  61.  
  62.   Furthermore the efun filter_array() examines the return value of each
  63.   call of the function the closure points to (the return value of the
  64.   write()s). If the value is true (not 0) then this element is put into
  65.   another array which filter_array() builds up. If the return value is
  66.   false (== 0) then this element is _not_ put into this array. When all
  67.   calls are done the slowly built up array is returned. Thus,
  68.   filter_array() filters from the given array all elements that the given
  69.   closure evaluates "true" for and returns an array of those. (The array
  70.   given to filter_array() itself is _not_ changed!)
  71.  
  72.   A senseful example for filter_array would be this:
  73.  
  74.     x = filter_array(users(),#'query_is_wizard);
  75.  
  76.   users() is an efun that gets no arguments and returns an array of all
  77.   logged in players (wizards and players). query_is_wizard() is a
  78.   simul_efun that gets an object as first (and only) argument and returns
  79.   true (1) if this object is a wizard and 0 otherwise.
  80.  
  81.   So, for each element of the array returned by users() the function
  82.   query_is_wizard() is called and only those for which 1 was returned are
  83.   collected into the result and then put into the variable x.
  84.  
  85.   We now have all logged in wizards stored as array in the variable x.
  86.  
  87.   Another example: We want to filter out all numbers that are greater than
  88.   42 from the array a of integers:
  89.  
  90.     x = filter_array(({ 10,50,30,70 }),#'>,42);
  91.  
  92.   (x will now be ({ 50,70 }).)
  93.  
  94.   Here two things are new: first: we create a closure that points to an
  95.   operator; second: we use the possibility to give extra arguments to
  96.   filter_array().
  97.  
  98.   Like all efuns the usual operators can be pointed to with a closure by
  99.   prepending #' to them. funcall(#'>,4,5) is exactly the same as (4>5).
  100.  
  101.   The extra arguments given as third to last argument (as many as you
  102.   like) to filter_array() are given as second to last argument to the
  103.   function pointed to by the closure each time it is called.
  104.  
  105.   Thus we now call (({ 10,50,30,70 })[0]>42), (({ 10,50,30,70 })[1]>42) ...
  106.   (which is (10>42), (50>42) ...) and return an array of all elements this
  107.   returns true for and store it into x.
  108.  
  109.   If you want to create a closure to an efun of which you have the name
  110.   stored in a string you can create such an efun-closure with the efun
  111.   symbol_function():
  112.  
  113.     symbol_function("write") // this will return #'write
  114.  
  115.     funcall(symbol_function("write"),"foobar");  // == write("foobar");
  116.  
  117.   This function does not very often occur in normal code but it is very
  118.   useful for tool-programming (eg the robe uses symbol_function() to allow
  119.   you call any efun you give).
  120.  
  121. 2 Lfun- and Lambda-Closures
  122.  
  123.   Very often the possibilities closures to efuns offer are not sufficient
  124.   for the purpose one has. In nearly all cases three possibilities exist in
  125.   such cases: use an lfun- or inline-closure, or a lambda-closure.
  126.  
  127. 2.1 Lfun-Closures
  128.  
  129.     The first possibility is rather easy: like with the efun-closures you
  130.     can create a pointer to a function in the same object you are by using
  131.     the #' to prepend it to a function name of a function declared above.
  132.     Example:
  133.  
  134.       status foo(int x) {
  135.         return ((x*2) > 42);
  136.       }
  137.  
  138.       int *bar() {
  139.         return filter_array(({ 10,50,30,70 }),#'foo);
  140.       }
  141.  
  142.     Thus, #'foo is used like there was an efun of this name and doing the
  143.     job that is done in foo().
  144.  
  145. 2.2 Inline Closure
  146.  
  147.     Inline closures are a variant of lfun closures, the difference being
  148.     that the function text is written right where the closure is used,
  149.     enclosed in a pair of '(:' and ':)'. The compiler will then take care
  150.     of creating a proper lfun and lfun-closure. The arguments passed to
  151.     such an inline closure are accessible by position: $1 would be the
  152.     first argument, $2 the second, and so on. With this, the
  153.     above example would read:
  154.  
  155.       int * bar() {
  156.         return filter_array(({ 10,50,30,70 }), (: ($1 * 2) > 42 :));
  157.       }
  158.  
  159.     or alternatively:
  160.  
  161.       int * bar() {
  162.         return filter_array(({ 10,50,30,70 }), (: return ($1 * 2) > 42; :));
  163.       }
  164.  
  165.     The difference between the two versions is that in the first form the text
  166.     of the inline closure must be an expression only, whereas in the second
  167.     form any legal statement is allowed. The compiler distinguishes the two
  168.     forms by the last character before the ':)': if it's a ';' or '}', the
  169.     compiler treats the closure as statement(s), otherwise as expression.
  170.  
  171.     Inline closures may also nested, so that the following (not very useful)
  172.     example is legal, too:
  173.  
  174.       return filter_array( ({ 10, 50, 30, 70 })
  175.                          , (: string *s;
  176.                               s = map_array(users(), (: $1->query_name() :));
  177.                               return s[random(sizeof(s))] + ($1 * 2);
  178.                             :));
  179.  
  180.     The notation of inline closures is modelled after the MudOS functionals,
  181.     but there are a few important differences in behaviour.
  182.  
  183.  
  184. 2.3 Lambda-Closures
  185.  
  186.     Lambda-Closures take the idea of 'define it where you use it' one step
  187.     further. On first glance they may look like inline closures with an uglier
  188.     notation, but they offer a few increased possibilities. But first things
  189.     first.
  190.  
  191.     The efun lambda() creates a function temporarily and returns a closure
  192.     pointing to this function. lambda() therefor gets two arrays as
  193.     arguments, the first is a list of all arguments the function shall expect
  194.     and the second array is the code of the function (in a more or less
  195.     complicated form; at least not in C- or LPC-syntax). The closure #'foo
  196.     from the example above could be notated as lambda-closure:
  197.  
  198.       lambda(({ 'x }),({ (#'>),
  199.                          ({ (#'*),'x,2 }),
  200.                          42
  201.                       }))
  202.  
  203.     Now, the first argument is ({ 'x }), an array of all arguments the
  204.     function shall expect: 1 argument (called 'x) is expected. Notice the
  205.     strange notation for this argument with one single leading tick. Like
  206.     The hash-tick to denote closures the leading tick is used to denote
  207.     things called "symbols". They do not differ much from strings and if
  208.     you do not want to have a deeper look into closures you can leave it
  209.     this way.
  210.  
  211.     The second argument is an array. The first element of such an array
  212.     must be an efun- or an lfun-closure, the further elements are the
  213.     arguments for the function this closure points to. If such an argu-
  214.     ment is an array, it is treated alike; the first element must be a
  215.     closure and the remaining elements are arguments (which of course
  216.     also might be arrays ...).
  217.  
  218.     This leads to a problem: sometimes you want to give an array as an
  219.     argument to a function. But arrays in an array given to lambda() are
  220.     interpreted as code-arrays. To allow you to give an array as an argu-
  221.     ment within an array given to lambda(), you can use the function
  222.     quote() to make your array to a quoted array (a quoted array is for
  223.     an array what a symbol is for a string):
  224.  
  225.       lambda(0,({ (#'sizeof),
  226.                   quote(({ 10,50,30,70 }))
  227.                }))
  228.  
  229.     This lambda-closure points to a function that will return 4 (it will
  230.     call sizeof() for the array ({ 10,50,30,70 })). Another thing: if
  231.     we want to create a function that expects no arguments, we can give
  232.     an empty array as first argument to lambda() but we can give 0 as
  233.     well to attain this. This is just an abbreviation.
  234.  
  235.     Lambda-closure constructs can become quite large and hard to read. The
  236.     larger they become the harder the code is to read and you should avoid
  237.     extreme cases. Very often the possibility to use an lfun or an inline
  238.     instead of a large lambda shortens the code dramatically. Example:
  239.  
  240.       status foo(object o) {
  241.         return environment(o)->query_level()>WL_APPRENTICE;
  242.       }
  243.  
  244.       x=filter_array(a,#'foo);
  245.  
  246.     does the same as
  247.  
  248.       x=filter_array(a,lambda(({ 'o }),
  249.                               ({ (#'>),
  250.                                  ({ (#'call_other),
  251.                                     ({ (#'environment),'o }),
  252.                                     "query_level"
  253.                                  }),
  254.                                  WL_APPRENTICE
  255.                               })));
  256.  
  257.     (Note that the syntax with the arrow "->" for call_other()s cannot be
  258.     used, #'-> does not exist. You have to use #'call_other for this and
  259.     give the name of the lfun to be called as a string.)
  260.  
  261.     This example also demonstrates the two disadvantages of lambda closures.
  262.     First, they are very difficult to read, even for a simple example like
  263.     this. Second, the lambda closure is re-created everytime the
  264.     filter_array() is executed, even though the created code is always the
  265.     same.
  266.  
  267.     'Why lambdas at all then?' you may ask now. Well, read on.
  268.  
  269.  
  270. 2.3.1 Advantages of Lambda Closures
  271.  
  272.     The advantages of lambdas stem from the fact that they are created
  273.     at runtime from normal arrays.
  274.  
  275.     This means that the behaviour of a lambda can be made dependant on data
  276.     available only at runtime. For example:
  277.  
  278.       closure c;
  279.       c = lambda(0, ({#'-, ({ #'time }), time() }) );
  280.  
  281.     Whenever you now call this closure ('funcall(c)') it will return the
  282.     elapsed time since the closure was created.
  283.  
  284.     The second advantage of lambdas is that the arrays from which they
  285.     are compiled can be constructed at runtime. Imagine a customizable prompt
  286.     which can be configured to display the time, the environment, or both:
  287.  
  288.       mixed code;
  289.  
  290.       code = ({ "> " });
  291.       if (user_wants_time)
  292.         code = ({ #'+, ({ #'ctime }), code });
  293.       if (user_wants_environment)
  294.         code = ({ #'+, ({#'to_string, ({#'environment, ({#'this_player }) }) })
  295.                      , code });
  296.       set_prompt(lambda(0, code));
  297.  
  298.  
  299. 2.3.2 Free Variables in Lambda-Closure Constructs
  300.  
  301.       You can use local variables in lambda constructs without declaring
  302.       them, just use them. The only limitation is that you at first have
  303.       to assign something to them. Give them as symbols like you do with
  304.       the arguments. This feature does not make much sense without the use
  305.       of complexer flow controlling features described below.
  306.  
  307.       The closure #'= is used to assign a value to something (like the
  308.       LPC-operator = is).
  309.  
  310. 2.3.3 Special Efun-Closures and Operator-Closures for Lambdas
  311.  
  312.       There are some special closures that are supposed to be used only
  313.       within a lambda construct. With them you can create nearly all code
  314.       you can with regular LPC-code like loops and conditions.
  315.  
  316.       #'? acts like the "if" statement in LPC. The first argument is the
  317.           condition, the second is the code to be executed if the condition
  318.           returns true. The following arguments can also be such couples of
  319.           code-arrays that state a condition and a possible result. If at
  320.           the end there is a single argument, it is used as the else-case
  321.           if no condition returned true.
  322.  
  323.           lambda(({ 'x }),({ (#'?),            // if
  324.                              ({ (#'>),'x,5 }), //    (x > 5)
  325.                              ({ (#'*),'x,2 }), //       result is x * 2;
  326.                              ({ (#'<),'x,-5 }) // else if (x < -5)
  327.                              ({ (#'/),'x,2 }), //    result is x/2;
  328.                              'x                // else result is x;
  329.                           }))
  330.  
  331.       #'?! is like the #'? but it negates all conditions after evaluation
  332.            and thus is like an ifnot in LPC (if there were one).
  333.  
  334.       #', (which looks a bit strange) is the equivalent of the comma-operator
  335.           in LPC and says: evaluate all arguments and return the value of
  336.           the last. It is used to do several things inside a lambda-closure.
  337.  
  338.           lambda(({ 'x }),({ (#',),  // two commas necessary!
  339.                                      // one for the closure and one as
  340.                                      // delimiter in the array
  341.                              ({ (#'write),"hello world!" }),
  342.                              ({ (#'say),"Foobar." })
  343.                           }))
  344.  
  345.       #'while acts like the LPC statement "while" and repeats executing one
  346.               code-array while another returns true.
  347.               #'while expects two or more arguments: the condition as first
  348.               argument, then the result the whole expression shall have after
  349.               the condition turns false (this is in many cases of no interest)
  350.               and as third to last argument the body of the loop.
  351.  
  352.               lambda(0,({ (#',),            // several things to do ...
  353.                           ({ (#'=),'i,0 }),    // i is a local variable of this
  354.                                                // lambda-closure and is
  355.                                                // initialized with 0 now.
  356.                           ({ (#'while),
  357.                              ({ (#'<),'i,10 }),   // condition: i < 10
  358.                              42,                  // result is not interesting,
  359.                                                   // but we must give one
  360.                              ({ (#'write),'i }),  // give out i
  361.                              ({ (#'+=),'i,1 })    // increase i
  362.                           })
  363.                        }))
  364.  
  365.                The function this closure points to will give out the
  366.                numbers from 0 to 9 and then return 42.
  367.  
  368.       #'do is like the do-while statement in LPC and is very much like the
  369.            #'while. The difference is that #'while tests the condition al-
  370.            ready before the body is evaluated for the first time, this means
  371.            that the body might not be evaluated even once. #'do evaluates
  372.            the body first and then the condition, thus the body is evaluated
  373.            at least one time.
  374.            Furthermore, the arguments for #'do are changed in order. #'do
  375.            expects as first to last but two the body of the loop, then the
  376.            condition (as last-but-one'th argument) and the result value as
  377.            last argument. So #'do must have at least two arguments: the
  378.            condition and the result.
  379.  
  380.            lambda(0,({ (#',),          // several things to do ...
  381.                        ({ (#'=),'i,0 }),  // i is a local variable of this
  382.                                           // lambda-closure and is initialized
  383.                                           // with 0 now.
  384.                        ({ (#'do),
  385.                           ({ (#'write),'i }),  // give out i
  386.                           ({ (#'+=),'i,1 })    // increase i
  387.                           ({ (#'<),'i,10 }),   // condition: i < 10
  388.                           42                   // result is not interesting
  389.                        })
  390.                     }))
  391.  
  392.       NOTE: There is no #'for in LPC, you should use #'while for this.
  393.  
  394.       #'foreach is like the foreach() statement in LPC. It evaluates one or
  395.            more bodies repeatedly for every value in a giving string, array
  396.            or mapping. The result of the closure is 0.
  397.  
  398.            #'foreach expects two or more arguments:
  399.             - a single variable symbol, or an array with several variable
  400.               symbols
  401.             - the value to iterate over
  402.             - zero or more bodes to evaluate in each iteration.
  403.  
  404.            The single values retrieved from the given value are assigned
  405.            one after another to the variable(s), then the bodies are executed
  406.            for each assignment.
  407.  
  408.            lambda(0, ({#'foreach, 'o, ({#'users})
  409.                                     , ({'#call_other, 'o, "die" })
  410.                      }));
  411.  
  412.            lambda(0, ({#'foreach, ({'k, 'v}), ({ ...mapping...})
  413.                                     , ({'#printf, "%O:%O\n", 'k, 'v })
  414.                      }));
  415.  
  416.       #'return gets one argument and acts like the "return" statement in LPC
  417.                in the function that is created by lambda(). It aborts the
  418.                execution of this function and returns the argument.
  419.  
  420.                lambda(0,({ (#'while),// loop
  421.                            1,        // condition is 1 ==> endles loop
  422.                            42,       // return value (which will never be used)
  423.                            ({ (#'write),"grin" })
  424.                            ({ (#'?!),               // ifnot
  425.                               ({ (#'random),10 }),  //       (random(10))
  426.                               ({ (#'return),100 })  //   return 100;
  427.                            })
  428.                         }))
  429.  
  430.                 This function will enter an endles loop that will in each
  431.                 turn give out "grin" and if random(10) returns 0 (which will
  432.                 of course happen very soon) it will leave the function with
  433.                 "return 100". The value 42 that is given as result of the
  434.                 loop would be returned if the condition would evaluate to 0
  435.                 which cannot be. (1 is never 0 ;-)
  436.  
  437.       #'break is used like the "break" statement in LPC and aborts the exe-
  438.               cution of loops and switches.
  439.               It must not appear outside a loop or switch of the lambda
  440.               closure itself, it cannot abort the execution of the function
  441.               the closure points to!
  442.  
  443.                 lambda(0,({ (#'?),
  444.                             ({ (#'random),2 }),
  445.                             ({ (#'break) }), // this will cause the error
  446.                                              // "Unimplemented operator break
  447.                                              // for lambda()"
  448.                             "random was false!"
  449.                          }));
  450.  
  451.               You can use ({ #'return,0 }) instead of ({ #'break }) in such
  452.               cases.
  453.  
  454.       #'continue is used like the "continue" statement in LPC and jumps to
  455.                  the end of the current loop and continues with the loop
  456.                  condition.
  457.  
  458.       #'default may be used within a #'switch-construct but be careful!
  459.                 To call symbol_function("default") (which is done usually
  460.                 by tools that allow closure-creation) might crash the
  461.                 driver! So please do only use it within your LPC-files.
  462.                 (NOTE: This driver bug is fixed somewhere below 3.2.1@131.)
  463.  
  464.       #'.. may be used within a #'switch-construct but is not implemented
  465.            yet (3.2.1@131). But #'[..] works well instead of it.
  466.  
  467.       #'switch is used to create closures which behave very much like the
  468.                switch-construct in LPC. To understand the following you
  469.                should already know the syntax and possibilities of the
  470.                latter one (which is mightier than the C-version of switch).
  471.  
  472.                I will confront some LPC versions and the corresponding clo-
  473.                sure versions below.
  474.  
  475.                LPC:                  Closure:
  476.                  switch (x) {          lambda(0,({ (#'switch), x,
  477.                  case 5:                           ({ 5 }),
  478.                    return "five";                  ({ (#'return),"five" }),
  479.                                                    (#',),
  480.                  case 6..9:                        ({ 6, (#'[..]), 9 }),
  481.                    return "six to nine";           ({ (#'return),
  482.                                                       "six to nine" }),
  483.                                                    (#',),
  484.                  case 1:                           ({ 1 }),
  485.                    write("one");                   ({ (#'write),"one" }),
  486.                    // fall through                 (#',),
  487.                  case 2:                           ({ 2,
  488.                  case 10:                             10 }),
  489.                    return "two or ten";            ({ (#'return),
  490.                                                       "two or ten" }),
  491.                                                    (#',),
  492.                  case 3..4:                        ({ 3, (#'[..]), 4 }),
  493.                    write("three to four");         ({ (#'write),
  494.                                                       "three to four" }),
  495.                    break;  // leave switch         (#'break),
  496.                  default:                          ({ (#'default) }),
  497.                    write("something else");        ({ (#'write),
  498.                                                       "something else" }),
  499.                    break;                          (#'break)
  500.                  }                              }))
  501.  
  502.       #'&& evaluates the arguments from the first on and stops if one evalu-
  503.            ates to 0 and returns 0. If none evaluates to 0 it returns the
  504.            result of the last argument.
  505.  
  506.       #'|| evaluates the arguments from the first on and stops if one evalu-
  507.            ates to true (not 0) and returns it. If all evaluate to 0 it
  508.            returns 0.
  509.  
  510.       #'catch executes the closure given as argument, but catches any
  511.            runtime error (see catch(E)). Optionally the symbol 'nolog
  512.            may be given as third argument to suppress the log of any
  513.            caught error.
  514.  
  515.  
  516. 2.4 Closures with Strange Names
  517.  
  518.     #'negate is the unary minus that returns -x for the argument x.
  519.  
  520.              map_array(({ 1,2,3 }),#'negate)
  521.  
  522.              This returns ({ -1,-2,-3 }).
  523.  
  524.     #'[ is used for the things that in LPC are done with
  525.              the []-operator (it indexes an array or a mapping).
  526.  
  527.              lambda(0,({ #'[,quote(({ 10,50,30,70 })),2 })) ==> 30
  528.              lambda(0,({ #'[,([ "x":10;50, "y":30;70 ]),"x",1 })) ==> 50
  529.  
  530.     #'[< is the same as #'[ but counts the elements from the
  531.                end (like indexing with [] and the "<").
  532.  
  533.     #'[..] returns a subarray of the argument from the one
  534.              given index to the other given index, both counted from the
  535.              beginning of the array.
  536.  
  537.     #'[..<]
  538.     #'[<..]
  539.     #'[<..<] same as above, but the indexes are counted from the end,
  540.  
  541.                 lambda(0,({ #'[..<],
  542.                             quote(({ 0,1,2,3,4,5,6,7 })),2,3
  543.                          }))
  544.  
  545.                 This will return ({ 2,3,4,5 }).
  546.     #'[..
  547.     #'[<.. same as above, but only the first index is given, the
  548.                subarray will go till the end of the original (like with
  549.                [x..]).
  550.  
  551.     #'({ is used to create arrays (as with ({ }) in LPC). All arguments
  552.              become the elements of the array.
  553.  
  554.              lambda(0,({ #'({,
  555.                          ({ (#'random),10 }),
  556.                          ({ (#'random),50 }),
  557.                          ({ (#'random),30 }),
  558.                          ({ (#'random),70 })
  559.                       }))
  560.  
  561.              This returns ({ random(10),random(50),random(30),random(70) }).
  562.  
  563.     #'([ is used to create mappings out of single entries (with seve-
  564.                ral values) like the ([ ]) in LPC. Very unusual is the fact
  565.                that this closure gets arrays as argument that are not eval-
  566.                uated, although they are not quoted.
  567.  
  568.                lambda(0,({ #'([,
  569.                            ({ "x",1,2,3 }),
  570.                            ({ "y",4,5,6 })
  571.                         }));
  572.  
  573.                This returns ([ "x": 1;2;3,
  574.                                "y": 4;5;6 ]).
  575.                
  576.                However, the elements of the arrays are evaluated as lambda
  577.                expressions, so if you want to create a mapping from values
  578.                evaluated at call time, write them as lambda closures:
  579.  
  580.                lambda(0, ({ #'([, ({ 1, ({ #'ctime }) }) }) )
  581.  
  582.                will return ([ 1: <result of ctime() at call time ]).
  583.  
  584.                Arrays can be put into the mapping by quoting:
  585.  
  586.                lambda(0, ({ #'([, ({ 1, '({ 2 }) }) }) )
  587.  
  588.                will return ([ 1: ({ 2 }) ])
  589.  
  590.  
  591.     #'[,] is nearly the same as #'[. The only difference
  592.                      shows up if you want to index a mapping with a width
  593.                      greater than 1 (with more than just one value per
  594.                      key) directly with funcall(). Example:
  595.                      funcall(#'[,([ 0:1;2, 3:4;5 ]),0,1)
  596.                      This will not work. Use #'[,] and it will
  597.                      work. If you want to use it in a lambda closure you
  598.                      do not have to use #'[,] and #'[ will
  599.                      do fine. On the other hand, #'[,] cannot
  600.                      work with arrays, so in nearly all cases use #'[
  601.                      and just in the described special case, use
  602.                      #'[,].
  603.                      This is a strange thing and I deem it a bug, so it
  604.                      might change in the future.
  605.  
  606. 2.5 Operator-Closures
  607.  
  608.     Most of the closures that are used for things which are done by opera-
  609.     tors are in fact not operator-closures but efun-closures. But there are
  610.     a few which do not have the state of efun-closures but are called
  611.     "operator-closures". #'return is an example, a complete list of them is
  612.     given below.
  613.  
  614.     These closures cannot be called directly using funcall() or apply() (or
  615.     other efuns like filter_array()), but must appear only in lambda-con-
  616.     structs.
  617.  
  618.       funcall(#'return,4);  // does not work! This will raise an
  619.                             // Uncallable-closure error.
  620.       funcall(lambda(0,     // this is a correct example
  621.                      ({ (#'return),4 })
  622.                     ));
  623.  
  624.     All operator-closures:
  625.     #'&&
  626.     #'||
  627.     #',
  628.     #'?
  629.     #'?!
  630.     #'break
  631.     #'continue
  632.     #'default
  633.     #'do
  634.     #'return
  635.     #'switch
  636.     #'while
  637.     #'({
  638.     #'([
  639.     #'[<..] (but not the other versions !)
  640.     #'!        (this really is an operator which could be a flaw, too;
  641.                 thanx to Fiona@Wunderland for reporting this :-)
  642.  
  643.     #'.. is very likely to be an operator closure too, but since it is
  644.     not implemented yet, I cannot say for sure.
  645.  
  646.     I also deem the fact that #'([<.. is an operator closure a bug.
  647.  
  648. 2.6 Variable-Closures
  649.  
  650.     All object-global variables might be "closured" by prepending a #' to
  651.     them to allow access and/or manipulation of them. So if your object has
  652.     a global variable x you can use #'x within a closure.
  653.  
  654.     Normally you will treat those expressions like lfun-closures: put them
  655.     into an array to get the value:
  656.  
  657.     object.c:
  658.               int x;
  659.               int foo() {
  660.                 return lambda(0,({ (#'write),({ (#'x) }) }));
  661.               }
  662.  
  663.     Anybody who now calls object->foo() will get a closure which will, when
  664.     evaluated, write the actual value of object's global variable x.
  665.  
  666. 3 Examples
  667.  
  668.   In this section I will give and explain some examples coming out of
  669.   praxis. If the explanation seems to be in some cases too detailed this
  670.   can be explained by the trial to allow the reader to read the examples
  671.   section first ;-)
  672.  
  673. 3.1 Lfun-Closure
  674.  
  675.     An item with a complex long-description like a watch that shall always
  676.     show the actual time will usually base upon the complex/item-class and
  677.     give an lfun-closure as argument to the set_long()-method.
  678.  
  679.     watch.c:
  680.              inherit "complex/item";
  681.  
  682.              string my_long() {
  683.                return ("The watch is small and has a strange otherworldly"
  684.                        " aura about it.\n"
  685.                        "The current time is: "+ctime()+".\n");
  686.              }
  687.  
  688.              void create() {
  689.                set_short("a little watch");
  690.                set_id(({ "watch","little watch" }));
  691.                set_long(#'my_long);  // the lfun-closure to the lfun my_long()
  692.              }
  693.  
  694. 3.2 Lambda-Closure
  695.  
  696.     The example from 3.1 can also be written using a lambda-closure.
  697.  
  698.     watch.c:
  699.              inherit "complex/item";
  700.  
  701.              void create() {
  702.                set_short("a little watch");
  703.                set_id(({ "watch","little watch" }));
  704.                set_long(lambda(0,({ (#'+),
  705.                                     "The watch is small and has a strange"
  706.                                     " otherworldly aura about it.\n"
  707.                                     "The current time is: ",
  708.                                     ({ (#'+),
  709.                                        ({ (#'ctime) }),
  710.                                        ".\n"
  711.                                     })
  712.                                  })));
  713.              }
  714.